#include "font.h"

static FT_Library library;

static int next_p2(int a)
{
    int rval = 1;
    while(rval < a)
        rval <<= 1;
    return rval;
}

int font_init(struct font* f, const char* fname)
{
    const FT_F26Dot6    char_height         = 20*64;
    const FT_F26Dot6    char_width          = 20*64;
    const FT_UInt       horz_resolution     = 150;
    const FT_UInt       vert_resolution     = 150;

    int rc, width, height, i, j, charnum, xsize = 0;
    GLubyte* expanded_data = NULL;

    memset(f, 0, sizeof(struct font));

    rc = FT_Init_FreeType(&library);
    if (rc != 0)
        goto err;

    rc = FT_New_Face(library, fname, 0, &f->face);
    if (rc != 0)
        goto err;

    rc = FT_Set_Char_Size(f->face, char_width, char_height, horz_resolution,  vert_resolution);
    if (rc != 0)
        goto err;

    f->n_tex = 256;
    f->tex = (GLuint*)malloc(sizeof(GLuint) * f->n_tex);
    if (f->tex == NULL)
    {
        rc = FT_Err_Out_Of_Memory;
        goto err;
    }

    f->advance =  (FT_Pos*)malloc(sizeof(FT_Pos) * f->n_tex);
    if (f->advance == NULL)
    {
        rc = FT_Err_Out_Of_Memory;
        goto err;
    }

    glGenTextures(f->n_tex, f->tex);

    for (charnum = 0; charnum < 256; charnum++)
    {
        rc = FT_Load_Char(f->face, charnum, FT_LOAD_RENDER);
        if (rc != 0)
            goto err;

        width = f->face->glyph->bitmap.width;
        height = f->face->glyph->bitmap.rows;
        f->advance[charnum] = f->face->glyph->advance.x;
//        width = next_p2(f->face->glyph->bitmap.width);
//        height = next_p2(f->face->glyph->bitmap.rows);
 
        rc = sizeof(GLubyte) * 2 * width * height;
        if (rc > xsize)
        {
            xsize = rc;
            expanded_data = (GLubyte*)realloc(expanded_data, xsize);
            if (expanded_data == NULL)
            {
                rc = FT_Err_Out_Of_Memory;
                goto err;
            }
        }

        for(j = 0; j <height ; j++)
        {
            for(i = 0; i < width; i++)
            {
                expanded_data[2 * (i + j * width)] = 255;
                expanded_data[2 * (i + j * width) + 1] =
                    (i >= f->face->glyph->bitmap.width || j >= f->face->glyph->bitmap.rows) ?
                        0 : f->face->glyph->bitmap.buffer[i + f->face->glyph->bitmap.width * j];
            }
        }
 
        glBindTexture(GL_TEXTURE_2D, f->tex[charnum]);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data);
    }

err:
    if (expanded_data != NULL)
        free(expanded_data);
    
    if (rc == 0)
        return 0;

//    glDeleteTextures();

    return rc;
}

struct point
{
    GLfloat x, y;
};

static const struct point tex_points[] = {
  { 0, 1 }, { 0, 0 },
  { 1, 1 }, { 1, 0 }
};

static const struct point vec_points[] = {
  { 0, 0 }, { 0, 1 },
  { 1, 0 }, { 1, 1 }
};

void font_draw(struct font* f, float x, float y, int c)
{
    glEnable(GL_TEXTURE_2D);
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
    glBindTexture(GL_TEXTURE_2D, f->tex[c]);

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	
    glVertexPointer(2, GL_FLOAT, sizeof(struct point), vec_points);

    glTexCoordPointer(2, GL_FLOAT, sizeof(struct point), tex_points);

    glPushMatrix();
    glTranslatef(x, y, 0);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glPopMatrix();

    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_BLEND);
    glEnable(GL_DEPTH_TEST);
}

void font_draw_str(struct font* f, float x, float y, const char* s)
{
    float sx = x;
    float sy = y;

    while (*s)
    {
        if (*s == '\n')
        {
            sy += ((float)f->advance['A'] / (float)20 / (float)64);
            sx = x;
        }   
        else
        {
            font_draw(f, sx, sy, *s);
            sx += ((float)f->advance[*s] / (float)20 / (float)64);
        }
        s++;
    }
}

int font_destroy(struct font* f)
{
    memset(f, 0, sizeof(struct font));
    return 0;
}
